﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace WimaxSimulator.Wimax
{
    [Serializable]
    public class BaseStation : Station, ISuperordinate
    {
        [NonSerialized]
        private int ab = 1;
        private int avaliableBandwidth
        {
            get
            {
                if (ab == 0)
                    ab = totalBandwidth;
                return ab;
            }
            set
            {
                ab = value;
            }
        }
        public int AvaliableBandwidth { get { return avaliableBandwidth; } }

        private int totalBandwidth;
        internal int TotalUlBandwidth
        {
            set
            {
                totalBandwidth = value;
                avaliableBandwidth = totalBandwidth;
            }
        }

        [NonSerialized]
        private List<ISubordinate> subordinates = new List<ISubordinate>();
        public List<ISubordinate> Subordinates
        {
            get
            {
                if (subordinates == null)
                    subordinates = new List<ISubordinate>();
                return subordinates;
            }
        }

        [NonSerialized]
        private Dictionary<ISubordinate, Queue<Packet>> downlinkQueue = new Dictionary<ISubordinate, Queue<Packet>>();
        public Dictionary<ISubordinate, Queue<Packet>> DownlinkQueue
        {
            get
            {
                if (downlinkQueue == null)
                    downlinkQueue = new Dictionary<ISubordinate, Queue<Packet>>();
                return downlinkQueue;
            }
        }

        public int DownlinkBandwidth { get; set; }

        [NonSerialized]
        private int totalSubordinateMs = 0;
        public int TotalSubordinateMS
        {
            get
            {
                return (from s in Subordinates where s is ISuperordinate select ((ISuperordinate)s).TotalSubordinateMS).Sum() + totalSubordinateMs;
                //return totalSubordinateMs;
            }
            //set { totalSubordinateMs = value; }
        }

        public Cidr IpAddressBlock { get; set; }

        [NonSerialized]
        private List<Cidr> ipAddressAssignment = new List<Cidr>();
        public List<Cidr> IpAddressAssignment
        {
            get
            {
                if (ipAddressAssignment == null)
                    ipAddressAssignment = new List<Cidr>();
                return ipAddressAssignment;
            }
        }
        public int Congestion
        {
            get
            {
                int congestion = 0;
                foreach (Queue<Packet> q in DownlinkQueue.Values)
                {
                    foreach (Packet p in q)
                    {
                        congestion += p.Size;
                    }
                }
                return congestion;
            }
        }
        private uint ip;
        public override uint Ip
        {
            get { return ip; }
        }

        public BaseStation(int Id, Cidr addressBlock, int totalUlBandwidth, int totalDlBandwidth, Point location, int coverageRadius)
            : base(Id, location, coverageRadius)
        {
            this.Id = Id;
            ip = addressBlock.Ip + 1;
            this.IpAddressBlock = addressBlock;
            this.totalBandwidth = totalUlBandwidth;
            avaliableBandwidth = totalUlBandwidth;
            DownlinkBandwidth = totalDlBandwidth;
        }

        public override uint Connect(ISubordinate subordinateStation)
        {
            if (subordinateStation.UplinkBandwidth < avaliableBandwidth)
            {
                uint ip = base.Connect(subordinateStation);
                if (ip > 0 && subordinateStation is MobileStation)
                    totalSubordinateMs++;
                return ip;
            }
            return 0;
        }





        public override void Receive(Packet p)
        {
            SimulationEngine.ReceivedData[Id] += p.Size;
            if (p.LastStation == SimulationEngine.Sink)
                TotalULReceived += p.Size;
            else
                TotalDLReceived += p.Size;
            p.LastStation = this;
            if (p.Destination.Ip == 0)
                return;
            if (IsSubordinate(IpAddressBlock, p.Destination.Ip))
            {
                //ISubordinate destQ = (from s in Subordinates
                //                      where (s is ISuperordinate
                //                      && (((ISuperordinate)s).IpAddressBlock.SubnetMask & s.CareOfAddress) == (((ISuperordinate)s).IpAddressBlock.SubnetMask & p.Destination.Ip))
                //                      || s.CareOfAddress == p.Destination.Ip
                //                      select s).SingleOrDefault<ISubordinate>();

                ISubordinate destQ = (from s in Subordinates
                                      where ~(s.CareOfAddress ^ p.Destination.Ip) == (from sub in Subordinates
                                                                                      select ~(sub.CareOfAddress ^ p.Destination.Ip)).Max<uint>()
                                      select s).SingleOrDefault<ISubordinate>();


                if (destQ == null)
                {

                    //SimulationEngine.Sink.Receive(p);
                    //TotalULSent += p.Size;
                    return;
                }
                DownlinkQueue[destQ].Enqueue(p);
            }
            else
            {
                SimulationEngine.Sink.Receive(p);
                TotalULSent += p.Size;
            }

        }

        public override void Send()
        {
            if (TotalSubordinateMS == 0)
                return;

            foreach (ISubordinate so in Subordinates)
            {
                Station s = (Station)so;
                int b = bandwidthForSubordinate(so) * (int)getMCS(s.Location);
                //if (s is ISuperordinate)
                //{
                //    b = bw * ((ISuperordinate)s).TotalSubordinateMS;
                //}
                while (true)
                {
                    if (DownlinkQueue[so].Count == 0)
                        break;
                    Packet p = DownlinkQueue[so].Peek();
                    if (p.Size < b)
                    {
                        DownlinkQueue[so].Dequeue();
                        b -= p.Size;
                        s.Receive(p);
                        TotalDLSent += p.Size;
                    }
                    else
                    {
                        if (b > 0)
                        {
                            Packet ps = new Packet(p.Source, p.Destination, b);//fragmentation
                            ps.LastStation = this;
                            p.Size -= b;
                            TotalDLSent += b;
                            s.Receive(ps);
                        }
                        break;
                    }
                }
            }
        }
        private int bandwidthForSubordinate(ISubordinate s)
        {
            if (DownlinkQueue[s].Count == 0)
                return 0;
            int totalToSend = (from q in DownlinkQueue.Values
                               select q.Count).Sum();
            return DownlinkBandwidth / totalToSend * DownlinkQueue[s].Count;
        }
        public bool BandwidthRequest(int bandwidth, bool relay)
        {
            if (avaliableBandwidth > bandwidth)
            {
                avaliableBandwidth -= bandwidth;
                return true;
            }
            return false;
        }

        public override void Clear()
        {
            base.Clear();
            avaliableBandwidth = totalBandwidth;
            totalSubordinateMs = 0;
        }
        public override string ToString()
        {
            return /*base.ToString() + "/" + IpAddressBlock.Subnet*/TotalSubordinateMS + " " + AvaliableBandwidth;
        }

        #region ISuperordinate Members

        public override void Disconnect(MobileStation mobileStation)
        {
            base.Disconnect(mobileStation);
            totalSubordinateMs--;
            avaliableBandwidth += mobileStation.UplinkBandwidth;
        }

        public void Disconnect(RelayStation relayStation, Cidr addressBlock)
        {
            this.avaliableBandwidth += relayStation.UplinkBandwidth;
            this.DownlinkQueue.Remove(relayStation);


            this.SentTrafficAnalysis.Remove(relayStation);
            ReceivedTrafficAnalysis.Remove(relayStation);
            IpAddressAssignment.Remove(addressBlock);

            Subordinates.Remove(relayStation);
        }
        public bool IsMySuper(ISuperordinate superOrdinate)
        {
            return false;
        }
        #endregion
        public void ReturnBandwidth(int bandwidth)
        {
            avaliableBandwidth += bandwidth;
        }
        public MCS MinMCS
        {
            get
            {
                return MCS.QAM64_3_4;
            }
            set
            {
                ;
            }
        }


    }
}
